home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 001a / mskrmsrc.zip / MSSRCV.ASM < prev    next >
Assembly Source File  |  1991-10-24  |  40KB  |  1,211 lines

  1.     NAME    mssrcv
  2. ; File MSSRCV.ASM
  3.     include mssdef.h
  4. ;       Copyright (C) 1982,1991, Trustees of Columbia University in the
  5. ;       City of New York.  Permission is granted to any individual or
  6. ;       institution to use, copy, or redistribute this software as long as
  7. ;       it is not sold for profit and this copyright notice is retained.
  8. ; Edit history
  9. ; 6 Sept 1991 version 3.11
  10. ; Last edit 19 Jan 1991
  11. ; Sliding Windows
  12.  
  13.     public    read2, read, rrinit, ackpak, nakpak, rstate
  14.  
  15. setattr equ    57h            ; DOS get/set file's date and time
  16.  
  17. data     segment
  18.     extrn    encbuf:byte, decbuf:byte, fmtdsp:byte, flags:byte, trans:byte
  19.     extrn    dtrans:byte, sstate:byte, diskio:byte, auxfile:byte
  20.     extrn    maxtry:byte, fsta:word, errlev:byte, kstatus:word
  21.     extrn    rpacket:byte, wrpmsg:byte, numpkt:word, windlow:byte
  22.     extrn    charids:word, windflag:byte, denyflg:word, chkparflg:byte
  23.  
  24. cemsg    db    'User intervention',0
  25. ermes6    db    'Unknown packet type',0
  26. erms11    db    'Not enough disk space for file',0
  27. erms13    db    'Unable to send reply',0
  28. erms14  db    'No response from the host',0
  29. erms15    db    'Error. No buffers in receive routine',0
  30. erms29    db    'Rejecting file: ',0
  31. erms30    db    'File size',0
  32. erms31    db    'Date/time',0
  33. erms32    db    'Mailer request',0
  34. erms33    db    'File Type',0
  35. erms34    db    'Transfer Char-set',0
  36. erms36    db    'Unknown reason',0
  37. infms1  db    cr,'           Receiving: In progress',cr,lf,'$'
  38. infms3  db      'Completed',cr,lf,'$'
  39. infms4  db      'Failed',cr,lf,'$'
  40. infms6  db      'Interrupted',cr,lf,'$'
  41. infms7    db    'Discarding $'
  42. donemsg    db    '100%$'
  43. filhlp2 db      ' Local path or filename or carriage return$'
  44. ender    db    bell,bell,'$'
  45. crlf    db    cr,lf,'$'
  46. badrcv    db    0        ; local retry counter
  47. filopn    db    0        ; non-zero if disk file is open
  48. ftime    db    0,0        ; file time (defaults to 00:00:00)
  49. fdate    db    0,0        ; file date (defaults to 1 Jan 1980)
  50. attrib    db    0        ; attribute code causing file rejection
  51. rstate    db    0        ; state of automata
  52. permchrset dw    0        ; permanent file character set holder
  53. temp    dw    0
  54. data    ends
  55.  
  56. code1    segment
  57.     extrn bufclr:far, pakptr:far, bufrel:far, makebuf:far, chkwind:far
  58.     extrn firstfree:far, getbuf:far, pakdup:far
  59.     extrn rpack:far, spack:far
  60. code1    ends
  61.  
  62. code    segment
  63.     extrn    gofil:near, comnd:near, cntretry:near
  64.     extrn    serini:near, spar:near, rpar:near
  65.     extrn    init:near, cxmsg:near, cxerr:near, perpos:near
  66.     extrn    ptchr:near, ermsg:near, winpr:near, dskspace:near
  67.     extrn    stpos:near, rprpos:near, packlen:near
  68.     extrn    dodec:near, doenc:near, errpack:near, intmsg:near
  69.     extrn    ihostr:near, prtasz:near, begtim:near
  70.     extrn    endtim:near, pktsize:near,strlen:near,strcpy:near
  71.     extrn    msgmsg:near, clrbuf:near, pcwait:near, goopen:near
  72.  
  73.     assume  cs:code, ds:data, es:nothing
  74.  
  75. ; Data structures comments.
  76. ; Received packet material is placed in buffers pointed at by [si].bufadr;
  77. ; SI is typically used as a pointer to a pktinfo packet structure.
  78. ; Sent packet material (typically ACK/NAKs) is placed in a standard packet
  79. ; structure named rpacket.
  80. ; Rpack and Spack expect a pointer in SI to the pktinfo structure for the
  81. ; packet.
  82.  
  83. ; RECEIVE command
  84.  
  85. READ    PROC    NEAR        
  86.     mov    bx,offset filhlp2    ; help message
  87.     mov    dx,offset auxfile    ; local file name string
  88.     mov    byte ptr auxfile,0    ; clear it first
  89.     mov    ah,cmword        ; local override filename/path
  90.     call    comnd        
  91.     jc    read1a            ; c = failure
  92.     mov    ah,cmeol        ; get a confirm
  93.     call    comnd
  94.     jc    read1a            ; c = failure
  95.     mov    rstate,'R'        ; set state to receive initiate
  96.     mov    flags.xflg,0
  97.     call    serini            ; initialize serial port
  98.     jnc    read1b            ; nc = success
  99.     or    errlev,ksrecv        ; set DOS error level
  100.     or    fsta.xstatus,ksrecv    ; set status, failed
  101.     or    kstatus,ksrecv        ; global status
  102.     test    flags.remflg,dquiet    ; quiet display mode?
  103.     jnz    read1a            ; nz = yes. Don't write to screen
  104.     mov    ah,prstr
  105.     mov    dx,offset infms4    ; Failed message
  106.     int    dos
  107.     stc
  108. read1a:    ret                ; return failure
  109.  
  110. read1b:    call    rrinit            ; init variables for read
  111.     call    clrbuf            ; clear serial port buffer
  112.     call    ihostr            ; initialize the host
  113.     cmp    flags.destflg,2        ; destination is screen?
  114.     je    read2            ; e = yes
  115.     call    init            ; setup display form
  116.     
  117.                     ; Called by GET & SRVSND, display ok
  118. READ2:    mov    kstatus,kssuc        ; global status, success
  119.     mov    windflag,0        ; init windows in use display flag
  120.     mov    numpkt,0        ; set the number of packets to zero
  121.     mov    fsta.pretry,0        ; clear total retry counter
  122.     call    begtim            ; start next statistics group
  123.     mov    flags.cxzflg,0        ; reset ^X/^Z flag
  124.     mov    ax,flags.chrset        ; permanent character set (Code Page)
  125.     mov    permchrset,ax        ; remember here around attributes ptks
  126.     cmp    fmtdsp,0        ; formatted display?
  127.     je    read2a            ; e = no
  128.     call    stpos
  129.     mov    ah,prstr        ; Receiving in progress msg
  130.     mov    dx,offset infms1
  131.     int    dos
  132. read2a:    jmp    dispatch
  133. READ    ENDP
  134.  
  135. ; Call the appropriate action routines for each state of the protocol machine.
  136. ; State is held in byte rstate. Enter at label dispatch.
  137.  
  138. dispatch proc    near            ; dispatch on state variable rstate
  139.     mov    ah,rstate        ; get current state
  140.     cmp    ah,'R'            ; Receive initiate state?
  141.     jne    dispat2            ; ne = no
  142.     call    rinit
  143.     jmp    short dispatch
  144.  
  145. dispat2:cmp    ah,'F'            ; File header receive state?
  146.     jne    dispat3
  147.     call    rfile            ; receive file header
  148.     jmp    short dispatch
  149.  
  150. dispat3:cmp    ah,'D'            ; Data receive state?
  151.     jne    dispat4
  152.     call    rdata            ; get data packets
  153.     jmp    short dispatch
  154.  
  155. dispat4:cmp    ah,'Z'            ; EOF?
  156.     jne    dispat5
  157.     call    reof            ; do EOF wrapup
  158.     jmp    short dispatch
  159.  
  160. dispat5:cmp    ah,'E'            ; ^C or ^E abort?
  161.     jne    dispat6            ; ne = no
  162.     call    bufclr
  163.     mov    bx,offset cemsg        ; user intervention message
  164.     call    errpack            ; send error message
  165.     call    intmsg            ; show interrupt msg for Control-C-E
  166.  
  167.                     ; Receive Complete state processor
  168. dispat6:cmp    rstate,'C'        ; completed normally?
  169.     jne    dispat6a        ; ne = no
  170.     cmp    flags.cxzflg,0        ; interrupted?
  171.     je    dispat7            ; e = no, ended normally
  172. dispat6a:or    errlev,ksrecv        ; set DOS error level
  173.     or    fsta.xstatus,ksrecv+ksuser ; set status, failed + intervention
  174.     or    kstatus,ksrecv+ksuser    ; global status
  175. dispat7:xor    ax,ax        ; tell statistics this is a receive operation
  176.     call    endtim            ; stop file statistics accumulator
  177.     call    bufclr            ; release all buffers
  178.     mov    windlow,0
  179.     mov    ax,permchrset        ; permanent character set (Code Page)
  180.     mov    flags.chrset,ax        ; restore external version
  181.     cmp    rstate,'C'        ; receive complete state?
  182.     je    dispat8            ; e = yes
  183.     or    errlev,ksrecv        ; Failed, set DOS error level
  184.     or    fsta.xstatus,ksrecv    ; set status, failed
  185.     or    kstatus,ksrecv        ; global status
  186.     call    fileclose        ; close output file
  187.     call    filedel            ; delete incomplete file
  188.  
  189. dispat8:cmp    flags.destflg,2        ; receiving to screen?
  190.     je    dispa11            ; e = yes, nothing to clean up
  191.     test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
  192.     jnz    dispa11            ; nz = yes, keep going
  193.     cmp    flags.xflg,0        ; writing to the screen?
  194.     jne    dispa11            ; ne = yes
  195.     call    stpos            ; position cursor to status line
  196.     mov    dx,offset infms3    ; completed message
  197.     cmp    rstate,'C'        ; receive complete state?
  198.     je    dispa10            ; e = yes
  199.     mov    dx,offset infms4    ; failed message
  200.     cmp    flags.cxzflg,0        ; interrupted?
  201.     je    dispa10            ; e = no, ended normally
  202.     mov    dx,offset infms6    ; interrupted message
  203. dispa10:mov    ah,prstr
  204.     int    dos
  205.     cmp    flags.belflg,0        ; bell desired?
  206.     je    dispa11            ; e = no
  207.     mov    ah,prstr
  208.     mov    dx,offset ender        ; ring the bell
  209.     int    dos
  210. dispa11:call    rprpos            ; put cursor at reprompt position
  211.     mov    flags.cxzflg,0        ; clear flag for next command
  212.     mov    auxfile,0        ; clear receive-as filename buffer
  213.     mov    flags.xflg,0        ; clear to-screen flag
  214.     mov    diskio.string,0        ; clear active filename buffer
  215.     mov    fsta.xname,0        ; clear statistics external name
  216.     clc                ; return to ultimate caller, success
  217.     ret
  218. dispatch endp
  219.  
  220. ;    Receive routines
  221.  
  222. ; Receive initiate packet (tolerates E F M S X Y types)
  223. RINIT    PROC    NEAR
  224.     mov    windlow,0        ; lowest acceptable packet number
  225.     mov    trans.chklen,1        ; Use 1 char for init packet
  226.     mov    chkparflg,1        ; check for unexpected parity
  227.     call    rcvpak            ; get a packet
  228.     jnc    rinit2            ; nc = success
  229.     ret
  230.  
  231. rinit2:    mov    ah,[si].pktype        ; examine packet type
  232.     cmp    ah,'S'            ; Send initiate packet?
  233.     je    rinit6            ; e = yes, process 'S' packet
  234.     cmp    ah,'M'            ; Message packet?
  235.     jne    rinit4            ; ne = no
  236.     call    msgmsg            ; display message
  237.     mov    trans.chklen,1        ; send Init checksum is always 1 char
  238.     call    ackpak0            ; ack and release packet
  239.     ret
  240.  
  241. rinit4:    cmp    ah,'F'            ; File receive?
  242.     je    rinit5            ; e = yes
  243.     cmp    ah,'X'            ; File receive to screen?
  244.     je    rinit5            ; e = yes
  245.     cmp    ah,'Y'            ; ACK to a REMOTE command?
  246.     jne    rinit4a            ; ne = no
  247.     call    msgmsg            ; show any message in the ACK
  248.     mov    rstate,'C'        ; Completed state
  249.     ret
  250.  
  251. rinit4a:cmp    ah,'N'            ; old NAK from a server?
  252.     jne    rinit4b            ; ne = no
  253.     call    bufrel            ; yes, release this packet buffer
  254.     ret                ;  and ignore it
  255.  
  256. rinit4b:mov    dx,offset ermes6    ; say unknown packet type
  257.     jmp    giveup            ; tell the other side
  258.  
  259. rinit5:    mov    rstate,'F'        ; File header receive state
  260.     ret
  261.  
  262.                     ; 'S' packet received
  263. rinit6:    call    spar            ; negotiate parameters
  264.     push    si
  265.     mov    si,offset rpacket    ; build response in this packet
  266.     call    rpar            ; report negotiated parameters
  267.     pop    si
  268.     mov    ah,trans.chklen        ; negotiated checksum length
  269.     push    ax            ; save it
  270.     mov    trans.chklen,1        ; use 1 char for init packet reply
  271.     mov    rstate,'F'        ; set state to file header
  272.     call    ackpak            ; ack with negotiated data
  273.     pop    ax            ; recover working checksum
  274.     mov    trans.chklen,ah
  275.     call    makebuf            ; remake buffering for new windowing
  276.     call    packlen            ; compute packet length
  277.     ret
  278. RINIT    ENDP
  279.  
  280.  
  281. ; Receive file header (tolerates E F M X Z types)
  282.  
  283. RFILE    PROC    NEAR
  284.     call    rcvpak            ; receive next packet
  285.     jnc    rfile1            ; nc = success
  286.     ret
  287.  
  288. rfile1: cmp    [si].pktype,'Z'        ; EOF?
  289.     jne    rfile2            ; ne = no, try next type
  290.     mov    rstate,'Z'        ; change to EOF state, SI is valid pkt
  291.     ret
  292.  
  293. rfile2: cmp    [si].pktype,'F'        ; file header (F or X packet)?
  294.     je    rfil3a            ; e = yes, 'F' pkt
  295.     cmp    [si].pktype,'X'        ; visual display header?
  296.     jne    rfile5            ; ne = neither one
  297.  
  298. rfile3:    mov    flags.xflg,1        ; 'X', say receiving to the screen
  299. rfil3a:    mov    filopn,0        ; assume not writing to a disk file
  300.     call    dodec            ; decode packet
  301.     call    cxmsg            ; clear Last Message line
  302.     call    begtim            ; start statistics gathering
  303.     mov    al,dtrans.xchset    ; reset Transmission char set
  304.     mov    trans.xchset,al        ;  to the current user default
  305.     mov    al,dtrans.xtype        ; ditto for File Type
  306.     mov    trans.xtype,al
  307.     mov    ax,permchrset        ; permanent character set (Code Page)
  308.     mov    flags.chrset,ax        ; active character set
  309.     call    gofil            ; open the output file
  310.     jnc    rfile4            ; nc = success
  311.     jmp    giveup            ; failure, dx has message pointer
  312.  
  313. rfile4:    push    si
  314.     push    di
  315.     mov    si,offset decbuf    ; local filename is here
  316.     mov    di,offset encbuf    ; destination is encoding buffer
  317.     mov    byte ptr [di],' '    ; leave space for protocol char
  318.     inc    di            ;  so other Kermits do not react
  319.     call    strcpy            ; copy it, to echo local name to host
  320.     dec    di
  321.     mov    dx,di
  322.     call    strlen            ; get length to cx for doenc
  323.     mov    si,offset rpacket    ; use this packet buffer
  324.     call    doenc            ; encode buffer, cx gets length
  325.     pop    di
  326.     pop    si
  327.     mov    rstate,'D'        ; set the state to data receive
  328.     jmp    ackpak            ; ack the packet, with filename
  329.  
  330. rfile5:    mov    ah,[si].pktype        ; get reponse packet type
  331.     cmp    ah,'B'            ; 'B' End Of Transmission?
  332.     jne    rfile6            ; ne = no
  333.     mov    rstate,'C'        ; set state to Complete
  334.     jmp    ackpak0            ; ack the packet
  335.  
  336. rfile6:    cmp    ah,'M'            ; Message packet?
  337.     jne    rfile7            ; ne = no
  338.     call    msgmsg            ; display message
  339.     jmp    ackpak0            ; ack packet, stay in this state
  340.  
  341. rfile7:    mov    dx,offset ermes6    ; unknown packet type
  342.     jmp    giveup            ; tell both sides and quit
  343. RFILE    ENDP
  344.  
  345. ; Get file attributes from packet
  346. ; Recognize file size in bytes and kilobytes (used if bytes missing),
  347. ; file time and date. Reject Mail commands. Return carry clear for success,
  348. ; carry set for failure. If rejecting place reason code in byte attrib.
  349.  
  350. GETATT    PROC    NEAR
  351.     mov    bx,[si].datadr        ; pointer to data field
  352. getat0:    push    bx
  353.     sub    bx,[si].datadr        ; bx = length to examine
  354.     cmp    bx,[si].datlen        ; are we beyond end of data?
  355.     pop    bx
  356.     jl    getat1            ; l = not yet
  357.     clc
  358.     ret                ; has carry clear for success
  359.  
  360. getat1:    cmp    byte ptr [bx],'1'    ; Byte length field?
  361.     jne    getat2            ; ne = no
  362.     test    flags.attflg,attlen    ; allowed to examine file length?
  363.     jnz    getat1a            ; nz = yes
  364.     jmp    getatunk        ; z = no, ignore
  365. getat1a:mov    al,[bx]            ; remember attribute
  366.     mov    attrib,al
  367.     inc    bx            ; pointer
  368.     push    si
  369.     call    getas            ; get file size
  370.     call    spchk            ; check available disk space
  371.     pop    si
  372.     jnc    getat0            ; nc = have enough space for file
  373.     ret                ; return failure
  374.  
  375. getat2:    cmp    byte ptr [bx],'!'    ; Kilobyte length field?
  376.     jne    getat3            ; ne = no
  377.     test    flags.attflg,attlen    ; allowed to examine file length?
  378.     jnz    getat2b            ; nz = yes
  379. getat2a:jmp    getatunk        ; z = no, ignore
  380. getat2b:mov    al,[bx]            ; remember attribute
  381.     mov    attrib,al
  382.     inc    bx            ; pointer
  383.     call    getak            ; get file size
  384.     jc    getat2a            ; carry means decode rejected
  385.     call    spchk            ; check available disk space
  386.     jnc    short getat0
  387.     ret                ; return failure
  388.  
  389. getat3:    cmp    byte ptr [bx],'#'    ; date field?
  390.     jne    getat4            ; ne = no
  391.     mov    word ptr ftime,0    ; clear time and date fields
  392.     mov    word ptr fdate,0
  393.     test    flags.attflg,attdate    ; allowed to update file date/time?
  394.     jnz    getat3a            ; nz = yes
  395.     jmp    getatunk        ; z = no, ignore
  396. getat3a:mov    al,[bx]            ; remember attribute
  397.     mov    attrib,al
  398.     inc    bx
  399.     call    getatd            ; get file date
  400.     jmp    short getat0
  401.  
  402. getat4:    cmp    byte ptr [bx],'+'    ; Disposition?
  403.     jne    getat5            ; ne = no
  404.     mov    al,[bx]            ; remember attribute
  405.     mov    attrib,al
  406.     cmp    byte ptr [bx+2],'M'    ; Mail indicator?
  407.     je    getat4c            ; e = yes, fail
  408.     cmp    byte ptr [bx+2],'P'    ; REMOTE PRINT?
  409.     jne    getat4b            ; ne = no, ignore field
  410.     test    flags.remflg,dserver    ; acting as a server now?
  411.     jz    getat4a            ; z = no
  412.     test    denyflg,prtflg        ; is this server command disabled?
  413.     jnz    getat4c            ; nz = yes, disabled
  414. getat4a:mov    word ptr diskio.string,'RP'    ; output to PRN
  415.     mov    word ptr diskio.string+2,'N'     ; ignore options
  416. getat4b:jmp    getatunk        ; ignore field
  417. getat4c:stc                ; set carry for failure
  418.     ret
  419.  
  420. getat5:    cmp    byte ptr [bx],'"'    ; File Type?
  421.     jne    getat6            ; ne = no
  422.            test    flags.attflg,atttype    ; allowed to examine file type?
  423.     jnz    getat5a            ; nz = yes
  424.     jmp    getatunk        ; z = no, ignore
  425. getat5a:mov    attrib,'"'        ; remember attribute
  426.     inc    bx            ; length field
  427.     xor    ch,ch
  428.     mov    cl,[bx]            ; get length
  429.     inc    bx
  430.     sub    cl,20h            ; remove ascii bias
  431.     jc    getat5d            ; c = error in length, fail
  432.     cmp    byte ptr [bx],'A'    ; Type letter (A, B, I), Ascii?
  433.     jne    getat5b            ; ne = no
  434.     mov    trans.xtype,0        ; say Ascii/Text file type
  435.     add    bx,cx            ; step to next field
  436.     jmp    getat0            ; next item please
  437. getat5b:cmp    byte ptr [bx],'B'    ; "B" Binary?
  438.     jne    getat5d            ; ne = no, fail
  439.     cmp    cl,2            ; full "B8"?
  440.     jb    getat5c            ; b = no, just "B"
  441.     cmp    byte ptr [bx+1],'8'    ; proper length?
  442.     jne    getat5d            ; ne = no
  443. getat5c:mov    trans.xtype,1        ; say Binary
  444.     add    bx,cx            ; step to next field
  445.     jmp    getat0            ; next item please
  446. getat5d:stc                ; set carry for rejection
  447.     ret
  448.  
  449. getat6:    cmp    byte ptr [bx],'*'    ; character set usage?
  450.     jne    getat6d            ; ne = no
  451.     test    flags.attflg,attchr    ; allowed to examine char-set?
  452.     jnz    getat6a            ; nz = yes
  453. getat6d:jmp    getatunk        ; z = no, ignore
  454. getat6a:mov    attrib,'*'        ; remember attribute
  455.     inc    bx            ; length field
  456.     xor    ch,ch
  457.     mov    cl,[bx]            ; get length
  458.     inc    bx
  459.     sub    cl,20h            ; remove ascii bias
  460.     js    getat6c            ; c = length error, fail
  461.     mov    trans.xchset,0        ; assume Transparent Transfer char-set
  462.     cmp    byte ptr [bx],'A'    ; Normal Transparent?
  463.     jne    getat6b            ; be = not Transparent
  464.     add    bx,cx            ; point at next field
  465.     clc
  466.     jmp    getat0
  467. getat6b:cmp    byte ptr [bx],'C'    ; character set?
  468.     je    getat7            ; e = yes
  469. getat6c:stc                ; set carry for rejection
  470.     ret
  471. getat7:    push    si            ; examine transfer character set
  472.     mov    si,bx            ; point at first data character
  473.     add    bx,cx            ; point bx beyond the text
  474.     dec    cx            ; deduct leading 'C' char from count
  475.     inc    si            ; skip the 'C'
  476.     push    bx            ; save bx
  477.     mov    bx,offset charids    ; point to array of char set info
  478.     mov    ax,[bx]            ; number of members
  479.     mov    temp,ax            ; loop counter
  480.     mov    trans.xchset,0        ; assume xfer char-set 0 (Transparent)
  481. getat7a:add    bx,2            ; point to a member's address
  482.     mov    di,[bx]            ; point at member [length, string]
  483.     cmp    cl,[di]            ; string lengths the same?
  484.     jne    getat7b            ; ne = no, try the next member
  485.     inc    di            ; point at ident string
  486.     cld
  487.     push    es
  488.     push    ds
  489.     pop    es            ; set es:di to data segment
  490.     push    cx            ; save incoming count
  491.     push    si            ; save incoming string pointer
  492.     repe    cmpsb            ; compare cx characters
  493.     pop    si
  494.     pop    cx
  495.     pop    es
  496.     jne    getat7b            ; ne = idents do not match
  497.     pop    bx            ; a match, use current trans.xchset
  498.     pop    si
  499.     cmp    trans.xchset,2        ; using Transfer Char Set Cyrillic?
  500.     jne    getat7d            ; ne = no
  501.     mov    flags.chrset,866    ; force CP866 (required by Cyrillic)
  502. getat7d:clc
  503.     jmp    getat0            ; success
  504. getat7b:inc    trans.xchset        ; try next set
  505.     dec    temp            ; one less member to consider
  506.     jnz    getat7a            ; nz = more members to try
  507.     pop    bx            ; failure to find a match
  508.     pop    si
  509.     mov    trans.xchset,0        ; use Transparent for unknown char set
  510.     cmp    flags.unkchs,0        ; keep the file?
  511.     je    getat7c            ; e = yes, regardless of unk char set
  512.     stc                ; set carry for rejection
  513.     ret
  514. getat7c:jmp    getat0            ; report success anyway
  515.  
  516.                     ; workers for above
  517. getatunk:inc    bx            ; Unknown. Look at length field
  518.     mov    al,[bx]
  519.     sub    al,' '            ; remove ascii bias
  520.     xor    ah,ah
  521.     inc    ax            ; include length field byte
  522.     add    bx,ax            ; skip to next attribute
  523.     jmp    getat0
  524.                     ; Decode File length (Byte) field
  525. getas:    mov    cl,[bx]            ; length of file size field
  526.     inc    bx            ; point at file size data
  527.     sub    cl,' '            ; remove ascii bias
  528.     xor    ch,ch
  529.     xor    ax,ax            ; current length, bytes
  530.     xor    dx,dx
  531.     jcxz    getas3            ; z = empty field
  532. getas2:    push    cx
  533.     shl    dx,1            ; high word of size, times two
  534.     mov    di,dx            ; save
  535.     shl    dx,1
  536.     shl    dx,1            ; times 8
  537.     add    dx,di            ; yields dx * 10
  538.     mov    di,dx            ; save dx
  539.     xor    dx,dx
  540.     mov    cx,10            ; also clears ch
  541.     mul    cx            ; scale up previous result in ax
  542.     mov    cl,[bx]            ; get a digit
  543.     inc    bx
  544.     sub    cl,'0'            ; remove ascii bias
  545.     add    ax,cx            ; add to current length
  546.     adc    dx,0            ; extend result to dx
  547.     add    dx,di            ; plus old high part
  548.     pop    cx
  549.     loop    getas2
  550.     mov    diskio.sizelo,ax    ; low order word
  551.     mov    diskio.sizehi,dx    ; high order word
  552.     clc
  553.     ret
  554. getas3:    dec    bx            ; backup
  555.     stc                ; fail the decode
  556.     ret
  557.                     ; Decode Kilobyte attribute
  558. getak:    mov    ax,diskio.sizelo    ; current filesize, low word
  559.     add    ax,diskio.sizehi
  560.     or    ax,ax            ; zero if not used yet
  561.     jz    getak1            ; z = not used before
  562.     dec    bx            ; backup pointer
  563.     stc                ; set carry to ignore this field
  564.     ret
  565.  
  566. getak1:    call    getas            ; parse as if Byte field
  567.     jnc    getak2            ; nc = parsed ok
  568.     ret                ; c = failure
  569. getak2:    mov    ax,diskio.sizelo    ; get low word of size
  570.     mov    dx,diskio.sizehi    ; high word
  571.     mov    dh,dl            ; times 256
  572.     mov    dl,ah
  573.     mov    ah,al
  574.     xor    al,al
  575.     shl    dx,1            ; times four to make times 1024
  576.     shl    dx,1
  577.     rol    ax,1            ; two high bits of ah to al
  578.     rol    ax,1
  579.     and    al,3            ; keep them
  580.     or    dl,al            ; insert into high word
  581.     xor    al,al
  582.     mov    diskio.sizehi,dx    ; store high word
  583.     mov    diskio.sizelo,ax    ; store low word
  584.     clc                ; clear carry
  585.     ret
  586.                     ; File date and time
  587. getatd:    mov    word ptr ftime,1    ; two seconds past midnight
  588.     mov    word ptr fdate,0
  589.     mov    dl,[bx]            ; field length
  590.     xor    dh,dh
  591.     sub    dl,' '            ; remove ascii bias
  592.     inc    bx            ; next field
  593.     add    dx,bx            ; where next field begins
  594.     mov    temp,dx            ; save in temp
  595.     cmp    byte ptr[bx+6],' '    ; short form date (yymmdd)?
  596.     je    getad2            ; e = yes
  597.     add    bx,2            ; skip century digits (19)
  598. getad2:    mov    ax,10
  599.     mov    dx,[bx]            ; get year tens and units digits
  600.     add    bx,2            ; dl has tens, dh has units
  601.     sub    dx,'00'            ; remove ascii bias
  602.     mul    dl            ; ax = high digit times ten
  603.     add    al,dh            ; units digit
  604.     sub    ax,80            ; remove rest of 1980 bias
  605.     jns    getad2a            ; ns = no sign = non-negative result
  606.     xor    ax,ax            ; don't store less than 1980
  607. getad2a:shl    al,1            ; adjust for DOS bit format
  608.     mov    fdate+1,al
  609.     mov    ax,[bx]            ; get month digits
  610.     add    bx,2
  611.     sub    ax,'00'            ; remove ascii bias
  612.     or    al,al            ; tens digit set?
  613.     jz    getad2b            ; z = no
  614.     add    ah,10            ; add to units digit
  615. getad2b:cmp    ah,8            ; high bit of month set?
  616.     jb    getad3            ; b = no
  617.     or    fdate+1,1
  618.     sub    ah,8            ; and deduct it here
  619. getad3:    mov    cl,5
  620.     shl    ah,cl            ; normalize months bits
  621.     mov    fdate,ah
  622.     mov    dx,[bx]            ; do day of the month
  623.     add    bx,2            ; dh has units, dl has tens digit
  624.     sub    dx,'00'            ; remove ascii bias
  625.     mov    ax,10
  626.     mul    dl            ; ax = ten times tens digit
  627.     add    al,dh            ; plus units digit
  628.     or    fdate,al
  629.     cmp    bx,temp            ; are we at the end of this field?
  630.     jae    getad5            ; ae = yes, prematurely
  631.     inc    bx            ; skip space separator
  632.     mov    ax,10            ; prepare for hours
  633.     mov    dx,[bx]            ; hh digits
  634.     add    bx,2
  635.     sub    dx,'00'            ; remove ascii bias
  636.     mul    dl            ; 10*high digit of hours
  637.     add    al,dh            ; plus low digit of hours
  638.     mov    cl,3            ; normalize bits
  639.     shl    al,cl
  640.     mov    ftime+1,al        ; store hours
  641.     inc    bx            ; skip colon
  642.     mov    ax,10            ; prepare for minutes
  643.     mov    dx,[bx]            ; mm digits
  644.     add    bx,2
  645.     sub    dx,'00'            ; remove ascii bias
  646.     mul    dl            ; 10*high digit of minutes
  647.     add    al,dh            ; plus low digit of minutes
  648.     xor    ah,ah
  649.     mov    cl,5            ; normalize bits
  650.     shl    ax,cl
  651.     or    ftime+1,ah        ; high part of minutes
  652.     mov    ftime,al        ; low part of minutes
  653.     cmp    bx,temp            ; are we at the end of this field
  654.     jae    getad5            ; ae = yes, quit here
  655.     inc    bx            ; skip colon
  656.     mov    ax,10            ; prepare for seconds
  657.     mov    dx,[bx]            ; ss digits
  658.     add    bx,2
  659.     sub    dx,'00'            ; remove ascii bias
  660.     mul    dl            ; 10*high digit of seconds
  661.     add    al,dh            ; plus low digit of seconds
  662.     shr    al,1            ; store as double-seconds for DOS
  663.     or    ftime,al        ; store seconds
  664. getad5:    ret
  665. GETATT    ENDP
  666.  
  667. ; Receive data (tolerates A D E M Z types)
  668.  
  669. RDATA    PROC    NEAR
  670.     call    rcvpak            ; get next packet
  671.     jnc    rdata1            ; nc = success
  672.     ret                ; else return to do new state
  673.  
  674. rdata1:    mov    ah,[si].pktype        ; check packet type
  675.     cmp    ah,'D'            ; Data packet?
  676.     je    rdata3            ; e = yes
  677.     cmp    ah,'A'            ; Attributes packet?
  678.     je    rdata4            ; e = yes
  679.     cmp    ah,'M'            ; Message packet?
  680.     jne    rdat2            ; ne = no
  681.     call    msgmsg            ; display message
  682.     jmp    ackpak0            ; ack the packet, stay in this state
  683.  
  684. rdat2:    cmp    ah,'Z'            ; EOF packet?
  685.     jne    rdat2a            ; ne = no
  686.     mov    rstate,'Z'        ; next state is EOF, do not ack yet
  687.     ret
  688.  
  689. rdat2a:    mov    dx,offset ermes6    ; Unknown packet type
  690.     jmp    giveup
  691.                     ; D data packets
  692. rdata3:    cmp    filopn,2        ; file opened yet?
  693.     je    rdata3b            ; e = yes
  694.     call    goopen            ; open it now
  695.     jnc    rdata3a            ; nc = success
  696.     jmp    giveup            ; failure, dx has message pointer
  697. rdata3a:mov    filopn,2        ; say file is open now
  698. rdata3b:call    ptchr            ; decode 'D' packet, output to file
  699.     jc    rdat3c            ; c = failure to write output
  700.     jmp    ackpak0            ; ack the packet, stay in this state
  701.  
  702. rdat3c:    mov    dx,offset erms11    ; cannot store all the data
  703.     jmp    giveup            ; tell the other side
  704.  
  705.                          ; 'A' packet, analyze        
  706. rdata4:    call    getatt            ; get file attributes from packet
  707.     mov    cx,0            ; reply length, assume 0/nothing
  708.     jnc    rdat4b            ; nc = success, attributes accepted
  709.     mov    cx,2            ; 2 bytes, declining the file
  710.     mov    encbuf,'N'        ; decline the transfer
  711.     mov    al,attrib        ; get attribute causing rejection
  712.     mov    encbuf+1,al        ; report rejection reason to sender
  713.     or    fsta.xstatus,ksrecv    ; set status, failed
  714.     mov    kstatus,ksrecv        ; global status, failed
  715.     mov    flags.cxzflg,'X'    ; set this in case host ignores 'N'
  716.     test    flags.remflg,dquiet    ; quiet display?
  717.     jnz    rdat4b            ; nz = yes
  718.     push    si
  719.     push    cx
  720.     push    ax
  721.     mov    dx,offset erms29    ; say rejecting the file
  722.     call    ermsg            ; show rejecting file, then reason
  723.     pop    ax
  724.     mov    dx,offset erms30
  725.     cmp    al,'1'            ; Byte count?
  726.     je    rdat4a            ; e = yes
  727.     cmp    al,'!'            ; Kilobyte count?
  728.     je    rdat4a            ; e = yes
  729.     mov    dx,offset erms31
  730.     cmp    al,'#'            ; Date and Time?
  731.     je    rdat4a            ; e = yes
  732.     mov    dx,offset erms32
  733.     cmp    al,'+'            ; Mail?
  734.     je    rdat4a            ; e = yes
  735.     mov    dx,offset erms33
  736.     cmp    al,'"'            ; File Type?
  737.     je    rdat4a
  738.     mov    dx,offset erms34
  739.     cmp    al,'*'            ; Transfer Char-set?
  740.     je    rdat4a
  741.     mov    dx,offset erms36    ; unknown reason
  742. rdat4a:    call    prtasz            ; display reason
  743.     pop    cx
  744.     pop    si
  745. rdat4b:    push    si
  746.     mov    si,offset rpacket    ; encode to this packet
  747.     call    doenc            ; do encoding
  748.     pop    si
  749.     jmp    ackpak            ; ACK the attributes packet
  750. rdata endp
  751.  
  752. ; End of File processor (expects Z type to have been received elsewhere)
  753. ; Enter with packet pointer in SI to a 'Z' packet.
  754. reof    proc    near            ; 'Z' End of File packet
  755.     cmp    flags.cxzflg,0        ; interrupted?
  756.     jne    reof3            ; ne = yes, no 100% done indicator
  757.     cmp    fmtdsp,0        ; formatted screen?
  758.     je    reof5            ; e = no, no message
  759.     cmp    wrpmsg,0        ; written Percentage done yet?
  760.     je    reof5            ; e = no
  761.     call    perpos            ; position cursor to percent done
  762.     mov    dx,offset donemsg    ; say 100%
  763.     mov    ah,prstr
  764.     int    dos
  765.     jmp    short reof5        ; file close common code
  766.  
  767. reof3:    call    intmsg            ; show interrupt msg on local screen
  768.     or    errlev,ksrecv        ; set DOS error level
  769.     or    fsta.xstatus,ksrecv+ksuser ; set status, failed + intervention
  770.     mov    kstatus,ksrecv+ksuser    ; global status
  771.     cmp    flags.cxzflg,'X'    ; kill one file?
  772.     jne    reof5            ; ne = no
  773.     mov    flags.cxzflg,0        ; clear ^X so next file survives
  774.                     ; common code for file closing
  775. reof5:    call    fileclose        ; close the file
  776.     call    dodec            ; decode incoming packet to decbuf
  777.     cmp    decbuf,'D'        ; is the data "D" for discard?
  778.     jne    reof7            ; ne = no, write out file
  779.     call    filedel            ; delete file incomplete file
  780.     or    errlev,ksrecv        ; set DOS error level
  781.     or    fsta.xstatus,ksrecv+ksuser ; set status, failed + intervention
  782.     mov    kstatus,ksrecv+ksuser    ; global status
  783.  
  784. reof7:    mov    rstate,'F'
  785.     call    ackpak0            ; acknowledge the packet
  786.     xor    ax,ax            ; tell statistics this was a receive
  787.     call    endtim
  788.     mov    diskio.string,0        ; clear file name
  789.     ret
  790. reof    endp
  791.  
  792. ; init variables for read
  793. rrinit    proc    near
  794.     call    makebuf            ; construct & clear all buffer slots
  795.     call    packlen            ; compute packet length
  796.     xor    ax,ax
  797.     mov    numpkt,ax        ; set the number of packets to zero
  798.     mov    windlow,al        ; starting sequence number of zero
  799.     mov    fsta.pretry,ax        ; set the number of retries to zero
  800.     mov    filopn,al        ; say no file opened yet
  801.     mov    windflag,al        ; windows in use init flag
  802.     mov    fmtdsp,al        ; no formatted display yet
  803.     mov    diskio.string,al    ; clear active filename buffer
  804.     mov    fsta.xname,al        ; clear statistics external name
  805.     ret
  806. rrinit    endp
  807.  
  808. ; Deliver packets organized by sequence number.
  809. ; Delivers a packet pointer in SI whose sequence number matches windlow.
  810. ; If necessary a new packet is requested from the packet recognizer. Failures
  811. ; to receive are managed here and may generate NAKs. Updates formatted screen.
  812. ; Store packets which do not match windlow, process duplicates and strays.
  813. ; Error packet and ^C/^E interrupts are detected and managed here.
  814. ; Return success with carry clear and SI holding the packet structure address.
  815. ; Return failure with carry set, maybe with a new rstate.
  816.  
  817. rcvpak    proc    near
  818.     mov    al,windlow        ; sequence number we want
  819.     call    pakptr            ; find pkt pointer with this seqnum
  820.     mov    si,bx            ; the packet pointer
  821.     jnc    rcvpa1a            ; nc = got one, else read fresh pkt
  822.     call    getbuf            ; get a new buffer address into si
  823.     jnc    rcvpa1            ; nc = success
  824.     mov    bx,offset erms15    ; insufficient buffers
  825.     jmp    giveup
  826.  
  827. rcvpa1:    call    winpr            ; show window slots in use
  828.     call    rpack            ; receive a packet, si has buffer ptr
  829.     jc    rcvpa2            ; c = failure to receive, analyze
  830.     inc    numpkt            ; increment the number of packets
  831.     cmp    flags.xflg,0        ; receiving to screen?
  832.     jne    rcvpa1a            ; ne = yes, skip displaying
  833.     cmp    flags.destflg,2        ; destination is screen?
  834.     je    rcvpa1a            ; e = yes
  835.     call    pktsize            ; report packet qty and size
  836. rcvpa1a:jmp    rcvpa6            ; success, validate
  837. ; ------------------- failure to receive any packet -------------------------
  838.                     ; Reception failed. What to do?
  839. rcvpa2:    call    cntretry        ; update retries, detect ^C, ^E
  840.     jc    rcvpa2a            ; c = exit now from ^C, ^E
  841.     call    bufrel            ; discard unused buffer
  842.     inc    badrcv            ; count receive retries
  843.     mov    al,badrcv        ; count # bad receptions in a row
  844.     cmp    al,maxtry        ; too many?
  845.     jb    rcvpa4            ; b = not yet, NAK intelligently
  846.     mov    dx,offset erms14    ; no response from host
  847.     jmp    giveup            ; tell the other side
  848.  
  849. rcvpa2a:call    bufrel            ; discard unwanted buffer
  850.     stc                ; set carry for failure
  851.     ret                ; move to Error state
  852.  
  853.                     ; do NAKing
  854. rcvpa4:    mov    al,windlow        ; Timeout or Crunched packet
  855.     add    al,trans.windo        ; find next slot after last good
  856.     dec    al
  857.     and     al,3fh            ; start at window high
  858.     mov    ah,-1            ; set a not-found marker
  859.     mov    cl,trans.windo        ; cx = number of slots to examine
  860.     xor    ch,ch
  861. rcvpa4a:call    pakptr            ; sequence number (in AL) in use?
  862.     jnc    rcvpa4b            ; nc = yes, stop here
  863.     mov    ah,al            ; remember seqnum of highest vacancy
  864.     dec    al            ; work backward in sequence numbers
  865.     and    al,3fh
  866.     loop    rcvpa4a
  867.  
  868. rcvpa4b:mov    al,ah            ; last-found empty slot (-1 = none)
  869.     cmp    ah,-1            ; found a vacant slot?
  870.     jne    rcvpa4c            ; ne = no, else use first free seqnum
  871.     call    firstfree        ; set AL to first open slot
  872.     jc    rcvpa4d            ; c = no free slots, an error
  873. rcvpa4c:mov    rpacket.seqnum,al    ; NAK this unused sequence number
  874.     call    nakpak            ; NAK using rpacket
  875.     jc    rcvpa4d            ; c = failure on sending operation
  876.     stc                ; rcv failure, stay in current state
  877.     ret
  878.  
  879. rcvpa4d:mov    dx,offset erms13    ; failure, cannot send reply
  880.     jmp    giveup            ; show msg, change states
  881. ; ------------------------- received a packet ------------------------------
  882.             ; remove duplicates, validate sequence number
  883. rcvpa6:    mov    badrcv,0        ; clear retry counter
  884.     cmp    [si].pktype,'E'        ; Error packet? Accept w/any seqnum
  885.     jne    rcvpa6a            ; ne = no
  886.     jmp    error            ; display message, change states
  887.  
  888. rcvpa6a:mov    al,[si].seqnum        ; this packet's sequence number
  889.     mov    rpacket.seqnum,al    ; save here for reply
  890.     call    pakdup            ; set ah to number of copies
  891.     cmp    ah,1            ; more than one copy?
  892.     jbe    rcvpa7            ; be = no, just one
  893.     call    bufrel            ; discard duplicate
  894.     mov    al,rpacket.seqnum    ; recover current sequence number
  895.     call    pakptr            ; get packet pointer for original
  896.     mov    si,bx            ; should not fail if pakdup works ok
  897.     jnc    rcvpa7            ; nc = ok, work on the original again
  898.     ret                ; say failure, stay in current state
  899.  
  900. rcvpa7:    call    chkwind            ; validate sequence number (cx=status)
  901.     jc    rcvpa7b            ; c = outside current window
  902.     mov    al,[si].seqnum        ; get sequence number again
  903.     cmp    al,windlow        ; is it the desired sequence number?
  904.     jne    rcvpa7a            ; ne = no, do not change states yet
  905. ;;;    mov    badrcv,0        ; clear retry counter
  906.     clc
  907.     ret                ; return success, SI has packet ptr
  908.  
  909. rcvpa7a:stc                ; not desired pkt, stay in this state
  910.     ret                ; do not increment retry counter here
  911.  
  912. rcvpa7b:or    cx,cx            ; inside previous window?
  913.     jg    rcvpa7c            ; g = outside any window, ignore it
  914.     cmp    [si].pktype,'I'        ; let 'I' and 'S' pkts be reported
  915.     je    rcvpa7d            ; even if in previous window, to
  916.     cmp    [si].pktype,'S'        ; accomodate lost ack w/data
  917.     je    rcvpa7d
  918.     call    ackpak0            ; previous window, ack and ignore it
  919.     stc                ; rcv failure, stay in current state
  920.     ret
  921.  
  922. rcvpa7d:mov    rstate,'R'        ; redo initialization when 'I'/'S'
  923.     stc                ;  are observed, keep current pkt
  924.     ret
  925.  
  926. rcvpa7c:call    bufrel            ; ignore packet outside of any window
  927.     stc                ; rcv failure, stay in current state
  928.     ret
  929. rcvpak    endp
  930.  
  931. ; Send ACK packet. Enter with rpacket data field set up.
  932. ; ACKPAK sends ack with data, ACKPAK0 sends ack without data.
  933. ackpak    proc    near            ; send an ACK packet
  934.     cmp    rpacket.datlen,0    ; really just no data?
  935.     jne    ackpa2            ; ne = no, send prepared ACK packet
  936. ackpak0:mov    rpacket.datlen,0    ; no data
  937.     cmp    flags.cxzflg,0        ; user interruption?
  938.     je    ackpa2            ; e = no
  939.     push    cx            ; yes, send the interrupt character
  940.     push    si
  941.     mov    si,offset rpacket
  942.     mov    cl,flags.cxzflg        ; send this so host knows about ^X/^Z
  943.     mov    encbuf,cl        ; put datum into the encode buffer
  944.     mov    cx,1            ; data size of 1 byte
  945.     call    doenc            ; encode, char count is in cx
  946.     pop    si
  947.     pop    cx
  948. ackpa2:    mov    rpacket.pktype,'Y'    ; ack packet
  949.     mov    rpacket.numtry,0
  950. ackpa3:    push    si
  951.     mov    si,offset rpacket
  952.     call    spack            ; send the packet
  953.     pop    si
  954.     jnc    ackpa4            ; nc = success
  955.     cmp    flags.cxzflg,'C'    ; Control-C abort?
  956.     je    ackpa3a            ; e = yes, quit now
  957.     cmp    flags.cxzflg,'E'    ; Control-E abort?
  958.     je    ackpa3a            ; e = yes, quit now
  959.     push    ax            ; send failure, retry
  960.     mov    ax,100            ; 0.1 sec
  961.     call    pcwait            ; small wait between retries
  962.     inc    rpacket.numtry
  963.     mov    al,rpacket.numtry
  964.     cmp    al,maxtry        ; exceeded retry limit?
  965.     pop    ax
  966.     jbe    ackpa3            ; be = ok to try again
  967.     mov    sstate,'A'        ; set states to abort
  968.     mov    rstate,'A'
  969.     mov    rpacket.numtry,0
  970.     mov    dx,offset erms13    ; unable to send reply
  971.     jmp    giveup
  972. ackpa3a:stc                ; set carry for failure
  973.     ret
  974.  
  975. ackpa4:    mov    al,rpacket.seqnum    ; success
  976.     mov    rpacket.datlen,0    ; clear old contents
  977.     call    pakptr            ; acking an active buffer?
  978.     jc    ackpa5            ; c = no such seqnum, stray ack
  979.     push    si
  980.     mov    si,bx            ; packet pointer from pakptr
  981.     call    bufrel            ; release ack'ed packet
  982.     pop    si
  983.     mov    rpacket.numtry,0
  984.     cmp    al,windlow        ; acking window low?
  985.     jne    ackpa5            ; ne = no
  986.     mov    al,windlow        ; yes, rotate the window
  987.     inc    al
  988.     and    al,3fh
  989.     mov    windlow,al
  990. ackpa5:    clc
  991.     ret
  992. ackpak    endp
  993.  
  994. ; Send a NAK. Uses rpacket structure.
  995. NAKPAK    proc    near
  996.     mov    rpacket.numtry,0
  997. nakpa2:    push    si
  998.     mov    si,offset rpacket
  999.     mov    [si].datlen,0        ; no data
  1000.     inc    fsta.nakscnt        ; count NAKs sent
  1001.         mov    [si].pktype,'N'        ; NAK that packet
  1002.     call    spack
  1003.     pop    si
  1004.     jc    nakpa3            ; c = failure
  1005.     mov    rpacket.numtry,0
  1006.     clc
  1007.     ret                ; return success
  1008.  
  1009. nakpa3:    cmp    flags.cxzflg,'C'    ; Control-C abort?
  1010.     je    nakpa3a            ; e = yes, quit now
  1011.     cmp    flags.cxzflg,'E'    ; Control-E abort?
  1012.     je    nakpa3a            ; e = yes, quit now
  1013.     push    ax            ; send failure, retry
  1014.     mov    ax,100            ; wait 0.1 second
  1015.     call    pcwait
  1016.     inc    rpacket.numtry        ; count attempts to respond
  1017.     mov    al,rpacket.numtry
  1018.     cmp    al,maxtry        ; tried enough times?
  1019.     pop    ax
  1020.     jbe    nakpa2            ; be = ok to try again
  1021.     mov    sstate,'A'        ; set states to abort
  1022.     mov    rstate,'A'
  1023.     mov    rpacket.numtry,0
  1024.     mov    dx,offset erms13    ; unable to send reply
  1025.     jmp    giveup
  1026. nakpa3a:stc
  1027.     ret                ; return failure
  1028. NAKPAK    ENDP
  1029.  
  1030. ; Close, but do not delete, output file. Update file attributes,
  1031. ; add Control-Z or Control-L, if needed.
  1032. fileclose proc    near
  1033.     cmp    filopn,0        ; is a file open?
  1034.     jne    filec0            ; ne = yes
  1035.     ret
  1036. filec0:    cmp    flags.xflg,0        ; receiving to screen?
  1037.     jne    filec2            ; ne = yes
  1038.     cmp    flags.destflg,1        ; destination is disk?
  1039.     jne    filec1            ; ne = no
  1040.     cmp    flags.eofcz,0        ; should we write a ^Z?
  1041.     je    filec1            ; e = no, keep going
  1042.     cmp    trans.xtype,0        ; test mode tranfer?
  1043.     jne    filec2            ; ne = no, binary, no ^Z
  1044.     push    si
  1045.     mov    rpacket.datlen,1    ; one byte to decode and write
  1046.     mov    si,rpacket.datadr    ; source buffer address
  1047.     mov    byte ptr[si],'Z'-40h    ; put Control-Z in buffer
  1048.     mov    si,offset rpacket    ; address for decoder
  1049.     call    ptchr            ; decode and write to output
  1050.     pop    si
  1051. filec1:    cmp    flags.destflg,0        ; file destination is printer?
  1052.     jne    filec2            ; ne = no, skip next part
  1053.     push    si
  1054.     mov    rpacket.datlen,1    ; one byte to decode and write
  1055.     mov    si,rpacket.datadr    ; source buffer address
  1056.     mov    byte ptr [si],'L'-40h    ; put Control-L (FF) in buffer
  1057.     mov    si,offset rpacket    ; address for decoder
  1058.     call    ptchr            ; decode and write to output
  1059.     pop    si
  1060. filec2:    mov    ah,write2        ; write to file
  1061.     xor    cx,cx            ; write 0 bytes to truncate length
  1062.     mov    bx,diskio.handle    ; file handle
  1063.     or    bx,bx            ; valid handle?
  1064.     jl    filec5            ; l = no
  1065.     int    dos
  1066.     xor    al,al            ; get device info
  1067.     mov    ah,ioctl
  1068.     int    dos
  1069.     test    dl,80h            ; bit set if handle is for a device
  1070.     jnz    filec4            ; nz = non-disk, no file attributes
  1071.                     ; do file attributes and close
  1072.     mov    cx,word ptr ftime    ; new time
  1073.     mov    dx,word ptr fdate    ; new date
  1074.     mov    word ptr fdate,0
  1075.     mov    word ptr ftime,0    ; clear current time/date attributes
  1076.     mov    ax,cx
  1077.     or    ax,dx
  1078.     jz    filec4            ; z = no attributes to set
  1079.     or    cx,cx            ; time set as null?
  1080.     jnz    filec3            ; nz = no
  1081.     inc    cl            ; two seconds past midnight
  1082. filec3:    mov    ah,setattr        ; set file date/time attributes
  1083.     mov    al,1            ; set, not get
  1084.     mov    bx,diskio.handle    ; file handle
  1085.     int    dos            ; end of file attributes
  1086. filec4:    mov    bx,diskio.handle    ; file handle
  1087.     push    dx            ; save dx
  1088.     mov    ah,close2        ; close file
  1089.     int    dos
  1090.     pop    dx
  1091.     mov    filopn,0        ; say file is closed
  1092. filec5:    ret
  1093. fileclose endp
  1094.  
  1095. ; Delete file whose asciiz name is in diskio.string
  1096. filedel    proc    near
  1097.     mov    dx,offset diskio.string    ; file name, asciiz
  1098.     xor    ax,ax
  1099.     cmp    diskio.string,al    ; filename present?
  1100.     je    filede2            ; e = no
  1101.     cmp    flags.abfflg,al        ; keep incomplete file?
  1102.     je    filede2            ; e = yes
  1103.     test    flags.remflg,dquiet    ; quiet display?
  1104.     jnz    filede1            ; nz = yes
  1105.     cmp    flags.xflg,al        ; receiving to screen?
  1106.     jne    filede1            ; ne = yes, no message
  1107.     push    dx
  1108.     call    cxmsg            ; clear Last message line
  1109.     mov    dx,offset infms7    ; saying Discarding file
  1110.     mov    ah,prstr
  1111.     int    dos
  1112.     pop    dx
  1113.     call    prtasz            ; show filename
  1114. filede1:mov    ah,del2            ; delete the file
  1115.     int    dos
  1116. filede2:ret
  1117. filedel    endp
  1118.  
  1119. ; Error exit. Enter with dx pointing to asciiz error message.
  1120. ; Sends 'E' Error packet and shows message on screen. Changes state to 'A'.
  1121. ; Always returns with carry set.
  1122. giveup    proc    near
  1123.     cmp    flags.destflg,2        ; receiving to the screen?
  1124.     je    giveu1            ; e = yes, no formatted display
  1125.     call    ermsg            ; show msg on error line
  1126. giveu1:    mov    bx,dx            ; set bx to error message
  1127.     call    errpack            ; send error packet just in case
  1128.     mov    rstate,'A'        ; change the state to abort
  1129.     stc                ; set carry
  1130.     ret
  1131. giveup    endp
  1132.  
  1133. ; ERROR sets abort state, positions the cursor and displays the Error message.
  1134.  
  1135. ERROR    PROC    NEAR
  1136.     mov    rstate,'A'        ; set state to abort
  1137.     call    dodec            ; decode to decbuf
  1138.     mov    dx,offset decbuf    ; where msg got decoded, asciiz
  1139.     call    ermsg            ; show string
  1140.     stc                ; set carry for failure state
  1141.     ret
  1142. ERROR    ENDP
  1143.  
  1144. ; Called by GETATT in receiver code to verify sufficient disk space.
  1145. ; Gets file path from diskio.string setup in mssfil, remote size in diskio
  1146. ; from getatt, and whether a disk file or not via ioctl on the file handle.
  1147. ; Returns carry clear if enough space.
  1148. spchk    proc    near            ; check for enough disk space
  1149.     push    ax
  1150.     push    bx
  1151.     push    cx
  1152.     push    dx
  1153.     mov    ah,ioctl        ; ask DOS about this file handle
  1154.     xor    al,al            ; get info
  1155.     mov    bx,diskio.handle
  1156.     int    dos
  1157.      test    dl,80h            ; handle is a disk file?
  1158.     jnz    spchk5b            ; nz = no, always enough space
  1159.     mov    ah,gcurdsk        ; get current disk
  1160.     int    dos
  1161.     add    al,'A'            ; make 0 == A
  1162.     mov    cl,al            ; assume this drive
  1163.     mov    dx,word ptr diskio.string ; filename used in open
  1164.     cmp    dh,':'            ; drive letter given?
  1165.     jne    spchk1            ; ne = no
  1166.     mov    cl,dl            ; get the letter
  1167.     and    cl,not 20h        ; convert to upper case
  1168. spchk1:    call    dskspace        ; calculate space into dx:ax
  1169.     jc    spchk6            ; c = error
  1170.     push    ax            ; save low word of bytes
  1171.     push    dx            ; save high word, dx:ax
  1172.     mov    dx,diskio.sizehi    ; high word of file size dx:ax
  1173.     mov    ax,diskio.sizelo    ; low word
  1174.     mov    cx,dx            ; copy size long word to cx:bx
  1175.     mov    bx,ax
  1176.     shr    bx,1            ; divide long word by two
  1177.     shr    cx,1
  1178.     jnc    spchk2            ; nc = no carry down
  1179.     or    bx,8000h        ; get carry down
  1180. spchk2:    shr    bx,1            ; divide by two again
  1181.     shr    cx,1
  1182.     jnc    spchk3
  1183.     or    bx,8000h        ; get carry down
  1184. spchk3:    shr    bx,1            ; divide long word by two
  1185.     shr    cx,1
  1186.     jnc    spchk4            ; nc = no carry down
  1187.     or    bx,8000h        ; get carry down
  1188. spchk4:    shr    bx,1            ; divide long word by two
  1189.     shr    cx,1
  1190.     jnc    spchk5            ; nc = no carry down
  1191.     or    bx,8000h        ; get carry down
  1192. spchk5:    add    ax,bx            ; form dx:ax = (17/16) * dx:ax
  1193.     adc    dx,cx
  1194.     pop    cx            ; high word of disk space
  1195.     pop    bx            ; low word
  1196.     sub    bx,ax            ; minus inflated file size, low word
  1197.     sbb    cx,dx            ;  and high word
  1198.     js    spchk6            ; s = not enough space for file
  1199. spchk5b:clc
  1200.     jmp    short spchk7        ; enough space
  1201. spchk6:    stc
  1202. spchk7:    pop    dx
  1203.     pop    cx
  1204.     pop    bx
  1205.     pop    ax
  1206.     ret
  1207. spchk    endp
  1208.  
  1209. code    ends 
  1210.     end
  1211.